﻿#pragma once

#include "include/cef_client.h"

#include <list>

class QtCEFHandler : public CefClient,
                     public CefDisplayHandler,
                     public CefLifeSpanHandler,
                     public CefLoadHandler,
                     public CefKeyboardHandler {
    struct window_rect {
        int x;
        int y;
        int w;
        int h;
    };

public:
    explicit QtCEFHandler();
    ~QtCEFHandler();

    // Provide access to the single global instance of this object.
    static QtCEFHandler* GetInstance();

    // CefClient methods:
    virtual CefRefPtr<CefDisplayHandler>  GetDisplayHandler() override { return this; }
    virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }
    virtual CefRefPtr<CefLoadHandler>     GetLoadHandler() override { return this; }
    virtual CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() override { return this; }

    // CefDisplayHandler methods:
    virtual void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) override;

    // CefLifeSpanHandler methods:
    virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
    virtual bool DoClose(CefRefPtr<CefBrowser> browser) override;
    virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;

    // CefLoadHandler methods:
    virtual void OnLoadError(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame,
                             ErrorCode errorCode, const CefString& errorText,
                             const CefString& failedUrl) override;

    // CefKeyboardHandler methods:
    virtual bool OnPreKeyEvent(CefRefPtr<CefBrowser> browser, const CefKeyEvent& event,
                               CefEventHandle os_event, bool* is_keyboard_shortcut);

    // 调整大小（如果在窗口还没有创建出来时就调整大小，则选缓存，稍后再调整）
    void resize_to(int x, int y, int width, int height) {
        m_window_rect.x = x;
        m_window_rect.y = y;
        m_window_rect.w = width;
        m_window_rect.h = height;

        HWND wnd = get_browser_window_handle();
        if (wnd) {
            // 已经有窗口了，直接改变大小
            ::MoveWindow(wnd, 0, 0, width, height, true);
        }
    }

    // 在主浏览窗口中，加载指定的 URL
    void to_url(const std::string& url) {
        // 第一个 browser 实例可能还没有创建出来，毕竟 CEF 没那么快
        // 此时，需要先把 url 保存起来，等待 browser 创建出来后再加载
        CefRefPtr<CefBrowser> main_browser = get_main_browser();
        if (main_browser) {
            CefString strUrl(url);
            main_browser->GetMainFrame()->LoadURL(strUrl);
        } else {
            m_url_to_load = url;
        }
    }

    // Request that all existing browser windows close.
    void close_all_browsers(bool force_close);

    bool is_closing() const { return m_is_closing; }
    bool can_quit() const { return m_can_quit; }

private:
    // 获取主浏览器（其实就是第一个被打开的那个 browser）
    CefRefPtr<CefBrowser> get_main_browser() {
        if (!m_browser_list.empty()) {
            return m_browser_list.front();
        }
        return nullptr;
    }

    // 获取到浏览器窗口的窗口句柄，用于操作浏览器窗口大小、位置等
    HWND get_browser_window_handle() {
        if (!m_browser_list.empty()) {   //如果集合不为空
            // 获取集合中的第一个 CefBrowser元素 ,获取它的 CefBrowserHost
            // 对象，然后再获取CefBrowserHost 对象中的 WindowHandle 即窗口句柄
            return m_browser_list.front()->GetHost()->GetWindowHandle();
        }
        return NULL;
    }

private:
    typedef std::list<CefRefPtr<CefBrowser>> BrowserList;

    // List of existing browser windows. Only accessed on the CEF UI thread.
    BrowserList m_browser_list;

    bool m_is_closing;
    bool m_can_quit;

    // 等待加载的 URL：用于临时保存在主浏览窗口还没有创建出来之前就要求加载的 URL 地址
    std::string m_url_to_load;
    // 待调整的大小
    window_rect m_window_rect;

    // Include the default reference counting implementation.
    IMPLEMENT_REFCOUNTING(QtCEFHandler);
};